package ie.dkit.java3Demulation.gui;

import ie.dkit.java3Demulation.objects.GraphicsObject;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

public class PropertyChanger extends JDialog {

	class JavaBeanPropertyTableModel extends AbstractTableModel implements
			TableModelListener {
		/**
		 * 
		 */
		private static final long serialVersionUID = 599428742719670230L;

		private final String[] columnNames = new String[] { "Name", "Type",
				"Value", "New Value" };

		private final Class[] columnTypes = new Class[] { String.class,
				String.class, Object.class, Object.class };

		public final boolean[] editableTypes = new boolean[] { false, false,
				false, true };

		private PropertyDescriptor[] m_BeanProperties;

		private GraphicsObject m_CurrentObject;

		public JavaBeanPropertyTableModel(GraphicsObject graphicsObject)
				throws java.beans.IntrospectionException {
			m_CurrentObject = graphicsObject;
			BeanInfo beaninfo = Introspector.getBeanInfo(graphicsObject
					.getClass());
			m_BeanProperties = beaninfo.getPropertyDescriptors();
			addTableModelListener(this);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#getColumnCount()
		 */
		public int getColumnCount() {
			return columnNames.length;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#getRowCount()
		 */
		public int getRowCount() {
			return m_BeanProperties.length;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
		 */
		public boolean isCellEditable(int rowIndex, int columnIndex) {
			return editableTypes[columnIndex];
		}

		public GraphicsObject getGraphicsObjectAt(int rowIndex) {
			try {
				PropertyDescriptor prop = m_BeanProperties[rowIndex];
				Method method = prop.getReadMethod();
				Object object = method.invoke(m_CurrentObject);
				if (object instanceof GraphicsObject) {
					GraphicsObject graphicsObject = (GraphicsObject) object;
					return graphicsObject;
				}
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return null;
		}

		public void setGraphicsObjectAt(int rowIndex,
				GraphicsObject graphicsObject) {
			try {
				PropertyDescriptor prop = m_BeanProperties[rowIndex];
				Method method = prop.getWriteMethod();
				if (method == null) {
					JOptionPane.showMessageDialog(null, "Not changeable");
					return;
				}
				method.invoke(m_CurrentObject, graphicsObject);
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.TableModel#getValueAt(int, int)
		 */
		public Object getValueAt(int rowIndex, int columnIndex) {
			PropertyDescriptor prop = m_BeanProperties[rowIndex];
			Method method;
			switch (columnIndex) {
			case 0:
				return prop.getName();
			case 1:
				method = prop.getReadMethod();
				if (method == null) {
					return "";
				} else {
					return method.getReturnType().getSimpleName();
				}
			case 2:
				method = prop.getReadMethod();
				if (method == null) {
					return "";
				} else {
					try {
						Object object = method.invoke(m_CurrentObject);
						if (object instanceof GraphicsObject) {
							GraphicsObject graphicsObject = (GraphicsObject) object;
							return graphicsObject.getName();
						}
						return object;
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
					return null;
				}
			case 3:
				// The Edit Field
				method = prop.getReadMethod();
				if (method == null) {
					return null;
				} else {
					try {
						JButton button;
						final Object object = method.invoke(m_CurrentObject);
						if (object == null) {
							return null;
						}
						if (object instanceof GraphicsObject) {
							button = new JButton("...");
							button.addMouseListener(new MouseAdapter() {
								public void mouseClicked(MouseEvent e) {
									try {
										new PropertyChanger(gui,
												(GraphicsObject) object);
									} catch (IntrospectionException e1) {
										JOptionPane
												.showMessageDialog(gui,
														"Can't display Property Dialog");
										e1.printStackTrace();
									}
								}
							});
							return button;
						}
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
					return null;
				}
			default:
				return null;
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object,
		 *      int, int)
		 */
		public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
			PropertyDescriptor prop = m_BeanProperties[rowIndex];
			if (editableTypes[columnIndex]) {
				switch (columnIndex) {
				case 0:
					break;
				case 1:
					break;
				case 2:
					break;
				case 3:
					// The Edit Field
					Method method = prop.getWriteMethod();
					if (method == null) {
						JOptionPane.showMessageDialog(null, "Not changeable");
						break;
					}
					Class<?>[] parameterTypes = method.getParameterTypes();
					try {
						if (parameterTypes[0] == Double.TYPE) {
							method.invoke(m_CurrentObject, Double
									.parseDouble((String) aValue));
						} else if (parameterTypes[0] == String.class) {
							method.invoke(m_CurrentObject, aValue);
						} else if (parameterTypes[0]
								.isInstance(GraphicsObject.class)) {
							System.out.println("Yieepppiieee");
						}
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
					break;
				default:
					break;
				}
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.AbstractTableModel#getColumnClass(int)
		 */
		public Class<?> getColumnClass(int columnIndex) {
			return columnTypes[columnIndex];
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.table.AbstractTableModel#getColumnName(int)
		 */
		public String getColumnName(int column) {
			return columnNames[column];
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see javax.swing.event.TableModelListener#tableChanged(javax.swing.event.TableModelEvent)
		 */
		public void tableChanged(TableModelEvent e) {
			if (e.getType() == TableModelEvent.UPDATE) {
				int row = e.getFirstRow();
				int column = e.getColumn();
				if (editableTypes[column]) {
					Object aValue = getValueAt(row, column);
					setValueAt(aValue, row, column);
				}
			}
		}
	}

	/**
	 * 
	 */
	private static final long serialVersionUID = 9065703670553758160L;

	private GUI gui;

	public PropertyChanger(GUI gui, GraphicsObject graphicsObject)
			throws IntrospectionException {
		super(gui, "Graphics Object Editor"
				+ (graphicsObject.getName() == null ? ""
						: (" for: " + graphicsObject.getName())), false);
		this.gui = gui;
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());
		final JTable jTable;
		final JavaBeanPropertyTableModel javaBeanPropertyTableModel;
		panel.add(new JScrollPane(jTable = new JTable(
				javaBeanPropertyTableModel = new JavaBeanPropertyTableModel(
						graphicsObject))), BorderLayout.CENTER);

		// Tweak the appearance of the table by manipulating its column model
		TableColumnModel colmodel = jTable.getColumnModel();

		// Set column widths
		colmodel.getColumn(0).setPreferredWidth(200);
		colmodel.getColumn(1).setPreferredWidth(200);
		colmodel.getColumn(2).setPreferredWidth(200);

		// Right justify the text in the first column
		TableColumn namecol = colmodel.getColumn(0);
		DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
		renderer.setHorizontalAlignment(SwingConstants.RIGHT);
		namecol.setCellRenderer(renderer);

		// Add the Button renderer
		namecol = colmodel.getColumn(3);
		JTableButtonRenderer buttonRenderer = new JTableButtonRenderer(
				new DefaultTableCellRenderer());
		namecol.setCellRenderer(buttonRenderer);

		jTable.addMouseListener(new JTableButtonMouseListener(jTable));

		jTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

		JPanel buttonPanel = new JPanel();
		JButton copyButton = new JButton("Copy");
		copyButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				PropertyChanger.this.gui.copyPaste = (GraphicsObject) javaBeanPropertyTableModel
						.getValueAt(jTable.getSelectedRow(), 2);
			}
		});
		buttonPanel.add(copyButton);
		JButton pasteButton = new JButton("Paste");
		pasteButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (PropertyChanger.this.gui.copyPaste != null) {
					javaBeanPropertyTableModel.setGraphicsObjectAt(jTable
							.getSelectedRow(),
							PropertyChanger.this.gui.copyPaste);
				}
			}
		});
		buttonPanel.add(pasteButton);
		panel.add(buttonPanel, BorderLayout.SOUTH);
		add(panel);
		pack();
		setVisible(true);
	}
}

class JTableButtonRenderer implements TableCellRenderer {
	private TableCellRenderer __defaultRenderer;

	public JTableButtonRenderer(TableCellRenderer renderer) {
		__defaultRenderer = renderer;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.table.TableCellRenderer#getTableCellRendererComponent(javax.swing.JTable,
	 *      java.lang.Object, boolean, boolean, int, int)
	 */
	public Component getTableCellRendererComponent(JTable table, Object value,
			boolean isSelected, boolean hasFocus, int row, int column) {
		if (value instanceof Component)
			return (Component) value;
		return __defaultRenderer.getTableCellRendererComponent(table, value,
				isSelected, hasFocus, row, column);
	}
}

class JTableButtonMouseListener implements MouseListener {
	private JTable __table;

	private void __forwardEventToButton(MouseEvent e) {
		TableColumnModel columnModel = __table.getColumnModel();
		int column = columnModel.getColumnIndexAtX(e.getX());
		int row = e.getY() / __table.getRowHeight();
		Object value;
		JButton button;
		MouseEvent buttonEvent;

		if (row >= __table.getRowCount() || row < 0
				|| column >= __table.getColumnCount() || column < 0)
			return;

		value = __table.getValueAt(row, column);

		if (!(value instanceof JButton))
			return;

		button = (JButton) value;

		buttonEvent = (MouseEvent) SwingUtilities.convertMouseEvent(__table, e,
				button);
		button.dispatchEvent(buttonEvent);
		// This is necessary so that when a button is pressed and released
		// it gets rendered properly. Otherwise, the button may still appear
		// pressed down when it has been released.
		__table.repaint();
	}

	public JTableButtonMouseListener(JTable table) {
		__table = table;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent e) {
		__forwardEventToButton(e);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent e) {
		__forwardEventToButton(e);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent e) {
		__forwardEventToButton(e);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent e) {
		__forwardEventToButton(e);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent e) {
		__forwardEventToButton(e);
	}
}
